package top.bbman.boot.redis.bean;

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;
import top.bbman.boot.redis.constant.PeaRedisBeanName;

import javax.annotation.Resource;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.*;

/**
 * redis hash operation
 *
 * @author 祥霸
 * @version 1.0
 */
@Component(PeaRedisBeanName.DIY_HASH_REDIS)
public class PeaHashRedis<HK, HV> {

    @Resource(name="redisTemplate")
    private HashOperations<String, HK, HV> hashOperations;

    @Resource
    private RedisTemplate<String, HV> redisTemplate;


    /**
     * Returns the number of key-value mappings in this map.
     *
     * @param key redis key
     * @return the number of key-value mappings in this map
     */
    public Long size(String key) {

        return hashOperations.size(key);

    }

    /**
     * Returns <tt>true</tt> if this map contains no key-value mappings.
     *
     * @param key redis key
     * @return <tt>true</tt> if this map contains no key-value mappings
     */
    public boolean isEmpty(String key) {
        return this.size(key) == 0L;
    }

    /**
     * Returns the value to which the specified key is mapped
     *
     * @param key redis key
     * @param hashKey hash key
     * @return value
     */
    public HV get(String key, HK hashKey) {
        return hashOperations.get(key, hashKey);
    }

    /**
     * returns <tt>true</tt> if this map contains a mapping for the
     * specified key.
     *
     * @param key redis key
     * @param hashKey hash key
     * @return <tt>true</tt> if this map contains a mapping for the specified
     */
    public boolean containsKey(String key, HK hashKey) {
        return hashOperations.hasKey(key, hashKey);
    }

    /**
     * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for the key, the old
     * value is replaced.
     *
     * @param key redis key
     * @param hashKey hash key
     * @param value value to be associated with the specified key
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     */
    public HV put(String key, HK hashKey, HV value) {
        List<String> keys = new ArrayList<>();
        keys.add(key);
        DefaultRedisScript<HV> redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(this.getHvClass());
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("scripts/map/put.lua")));
        return redisTemplate.execute(redisScript, keys, hashKey, value);
    }

    /**
     * Copies all of the mappings from the specified map to this map.
     * These mappings will replace any mappings that this map had for
     * any of the keys currently in the specified map.
     *
     * @param key redis key
     * @param m map
     */
    public void putAll(String key, Map<? extends HK, ? extends HV> m) {
        hashOperations.putAll(key, m);
    }

    public HV remove(String key, HK hashKey) {
        List<String> keys = new ArrayList<>();
        keys.add(key);
        DefaultRedisScript<HV> redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(this.getHvClass());
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("scripts/map/remove.lua")));
        return redisTemplate.execute(redisScript, keys, hashKey);
    }

    /**
     * Returns a {@link Set} view of the keys contained in this map.
     *
     * @param key redis key
     * @return a set view of the keys contained in this map
     */
    public Set<HK> keySet(String key) {
        return hashOperations.keys(key);
    }

    /**
     * Returns a {@link Collection} view of the values contained in this map.
     *
     * @param key redis key
     * @return a view of the values contained in this map
     */
    public List<HV> values(String key) {
        return hashOperations.values(key);
    }

    /**
     * Returns a {@link Set} view of the mappings contained in this map.
     *
     * @param key redis key
     * @return a set view of the mappings contained in this map
     */
    public Set<Map.Entry<HK,HV>> entrySet(String key) {
        return this.entries(key).entrySet();
    }

    public Map<HK, HV> entries(String key) {
        return hashOperations.entries(key);
    }

    private Class<HV> getHvClass() {
        return (Class<HV>) Object.class;
    }

    private Class<HK> getHkClass() {
        return (Class<HK>) Object.class;
    }
}
